// memory allocation and freeing:

// TODO improve memory usage tracing
// TODO implement a garbage collector

void* alloc_mem (int size) {
   void *ret = calloc(size, 4);
   if (!ret)
      _error("couldn't allocate memory");
   return ret;
}

void free_mem (void* pointer) {
   free(pointer);
}

// duplicating and freeing:

Atom* duplicate_atom (Atom* at) {
   if (!at) return NULL;
   if (is_simple(at)) {
      if (at->type == SYMB)
         return create_symbol(duplicate_symbol(at->symb));
      return create_int(at->num);
   }
   Atom *na = create_atom(at->type);
   na->list = duplicate_list(at->list);
   return na;
}

List* duplicate_list (List* li) {
   if (!li) return NULL;
   List *nli = cons(NULL, NULL);
   List *np = nli;
   while (li) {
      if (li->head) np->head = duplicate_atom(li->head);
      if (li->tail) {
         np->tail = cons(NULL, NULL);
         np = np->tail;
      }
      li = li->tail;
   }
   return nli;
}

Table* duplicate_table (Table* ta) {
   if (!ta) return (Table*)_warning("DUPLICATE_TABLE" DUPLICATE_NULL);
   Table *nt = (Table*)ALLOC(sizeof(Table));
   Table *ntp = nt;
   while (ta) {
      ntp->key = duplicate_symbol(ta->key);
      ntp->value = duplicate_atom(ta->value);
      ntp->next = NULL;
      if (ta->next) {
         ntp->next = (Table*)ALLOC(sizeof(Table));
         ntp = ntp->next;
      }
      ta = ta->next;
   }
   return nt;
}

int* duplicate_symbol (int* symb) {
   if (symb == NULL)
      return NULL;
   int *ns = (int*)ALLOC(symb[0]+1);
   for (int a = 0; a <= symb[0]; a ++) {
      ns[a] = symb[a];
   }
   return ns;
}

void free_atom (Atom* at) {
   if (!at)
      return;
   if (is_simple(at)) {
      if (at->type == SYMB) FREE(at->symb);
   } else {
      free_list(at->list);
   }
   FREE(at);
}

void free_list (List* li) {
   if (!li) return;
   if (li->head) free_atom(li->head);
   if (li->tail)
      free_list(li->tail);
   FREE(li);
}

void free_table (Table* ta) {
   if (!ta) {
      _warning("FREE_TABLE" FREE_NULL);
      return;
   }
   FREE(ta->key);
   if (ta->value)
      free_atom(ta->value);
   if (ta->next)
      free_table(ta->next);
   FREE(ta);
}

// creating objects:

List* cons (Atom* he, List* ta) {
   List *nl = (List*)ALLOC(sizeof(List));
   nl->head = he;
   nl->tail = ta;
   return nl;
}

Atom* create_atom (Type _type) {
   Atom *at = (Atom*)ALLOC(sizeof(Atom));
   at->type = _type;
   return at;
}

Atom* create_int (int in) {
   Atom *at = create_atom(NUM);
   at->num = in;
   return at;
}

Atom* create_symbol (int* symb) {
   Atom *at = create_atom(SYMB);
   at->symb = symb;
   return at;
}

Atom* create_list (List* li) {
   if (li == NULL)
      return NULL;
   Atom *at = create_atom(LIST);
   at->list = li;
   return at;
}

Atom* create_lambda (List* la) {
   if (la == NULL)
      return NULL;
   Atom *at = create_atom(LAMBDA);
   at->list = la;
   return at;
}
